#include "log_file.h"
#include "os_sys.h"

#ifdef USE_RAM_AS_LOGFILE

uint8_t *g_pu8LogData = NULL;
LogHeader_t g_stLogHeader = {0};

static int32_t RamFileOpen() {
  if (!g_pu8LogData) {
    g_pu8LogData = RQ_MALLOC(MAX_LOG_FILE_SIZE);
  }
  if (!g_pu8LogData) {
    log_e("log data allocated failed");
    return -1;
  }
  return 0;
}

static int32_t RamFileClose() { return 0; }

static int32_t RamFileWriteHeader(LogHeader_t *header) {
  if (!header) {
    log_e("log file header is null");
    return 0;
  }
  memcpy(g_pu8LogData, header, sizeof(LogHeader_t));
  return sizeof(LogHeader_t);
}

static int32_t RamFileReadHeader(LogHeader_t *header) {
  if (!header) {
    log_e("log file header is null");
    return 0;
  }
  memcpy(header, g_pu8LogData, sizeof(LogHeader_t));
  return sizeof(LogHeader_t);
}

static int32_t ram_file_read(uint32_t addr, void *buf, uint32_t len) {
  uint32_t read_cnt = 0;
  if (!buf) {
    log_e("file read buffer is null");
    return 0;
  }
  if (!g_pu8LogData) {
    log_e("log data is invalid");
    return 0;
  }
  if (addr >= g_stLogHeader.total_len) {
    log_v("invalid log file read address");
    return 0;
  }
  if (addr + len > g_stLogHeader.total_len) {
    read_cnt = g_stLogHeader.total_len - addr;
  } else {
    read_cnt = len;
  }
  memcpy(buf, g_pu8LogData + addr + sizeof(LogHeader_t), read_cnt);
  return read_cnt;
}

static int32_t ram_file_write(uint32_t addr, void *buf, uint32_t len) {
  uint32_t write_cnt = 0;
  if (!buf) {
    log_e("file write buffer is null");
    return 0;
  }
  if (!g_pu8LogData) {
    log_e("log data is invalid");
    return 0;
  }
  if (addr >= MAX_LOG_FILE_SIZE) {
    log_v("invalid log file read address");
    return 0;
  }
  if (addr + len > MAX_LOG_FILE_SIZE) {
    write_cnt = MAX_LOG_FILE_SIZE - addr;
  } else {
    write_cnt = len;
  }
  memcpy(g_pu8LogData + addr + sizeof(LogHeader_t), buf, write_cnt);
  return write_cnt;
}

int32_t LogFile_GetHeader() {
  memset(&g_stLogHeader, 0, sizeof(g_stLogHeader));
  if (RamFileOpen() != 0) {
    log_e("get header: open log file failed");
    return -1;
  }
  uint32_t ret = RamFileReadHeader(&g_stLogHeader);
  if (ret == 0) {
    log_v("read log file failed");
    return -1;
  }

  if (g_stLogHeader.total_len >= MAX_FILE_LOG_LEN) {
    log_d("total_len:%d out of memory,reset to 0.", g_stLogHeader.total_len);
    g_stLogHeader.total_len = 0;
  }

  if (g_stLogHeader.offset >= MAX_FILE_LOG_LEN) {
    log_d("offset:%d out of memory,reset to 0.", g_stLogHeader.offset);
    g_stLogHeader.offset = 0;
  }

  log_d("offset:%d total_len:%d.\n", g_stLogHeader.offset,
        g_stLogHeader.total_len);
  RamFileClose();
  return 0;
}

int32_t LogFile_SyncCache(char *cache_data, int32_t cache_size) {
  int32_t write_len = 0;
  log_d("LogFile_SyncCache enter, cache_size:%d.\n", cache_size);
  if (RamFileOpen() != 0) {
    log_e("get header: open log file failed");
    return -1;
  }
  if (g_stLogHeader.offset + cache_size > MAX_FILE_LOG_LEN) {
    log_w("file_log_len:%d out of memory,reset to 0\n",
          g_stLogHeader.offset + cache_size);
    int32_t cache_write_len = MAX_FILE_LOG_LEN - g_stLogHeader.offset;
    write_len =
        ram_file_write(g_stLogHeader.offset, cache_data, cache_write_len);
    if (write_len < 0) {
      log_e("write log file failed");
      return -1;
    }

    g_stLogHeader.offset = 0;
    write_len =
        ram_file_write(g_stLogHeader.offset, cache_data + cache_write_len,
                       cache_size - cache_write_len);
    if (write_len < 0) {
      log_e("write log file failed");
      return -1;
    }

    g_stLogHeader.offset += write_len;
    g_stLogHeader.total_len = MAX_FILE_LOG_LEN;

  } else {
    int32_t write_len =
        ram_file_write(g_stLogHeader.offset, cache_data, cache_size);
    if (write_len < 0) {
      log_e("write log file failed");
      return -1;
    }
    g_stLogHeader.offset += write_len;
    g_stLogHeader.total_len += write_len;
  }

  write_len = RamFileWriteHeader(&g_stLogHeader);
  if (write_len < 0) {
    log_e("write log file failed");
    return -1;
  }
  RamFileClose();
  log_d("offset:%d, total_len:%d.\n", g_stLogHeader.offset,
        g_stLogHeader.total_len);
  return 0;
}

int32_t LogFile_GetData(char *buffer, uint32_t buff_size, uint32_t log_start,
                        uint32_t log_count) {
  uint32_t read_count = 0;
  uint32_t start_addr = 0;
  if ((!buffer) || (buff_size == 0)) {
    log_e("buffer is not valid");
    return -1;
  }
  if (RamFileOpen() != 0) {
    log_e("get header: open log file failed");
    return -1;
  }
  uint32_t save_log_cnt = g_stLogHeader.total_len / sizeof(LogInfo_t);
  if (log_start > save_log_cnt) {
    log_e("log start is not valid");
    return -1;
  }
  if (log_start + log_count > save_log_cnt) {
    read_count = save_log_cnt - log_start;
  } else {
    read_count = log_count;
  }
  if (read_count * sizeof(LogInfo_t) > buff_size) {
    read_count = buff_size / sizeof(LogInfo_t);
  }
  if (g_stLogHeader.total_len == MAX_FILE_LOG_LEN) {
    start_addr = log_start * sizeof(LogInfo_t) + g_stLogHeader.offset;
  } else {
    start_addr = log_start * sizeof(LogInfo_t);
  }
  ram_file_read(start_addr, buffer, read_count * sizeof(LogInfo_t));
  return read_count * sizeof(LogInfo_t);
}

uint32_t LogFile_GetCount() {
  return g_stLogHeader.total_len / sizeof(LogInfo_t);
}

uint32_t LogFile_Clear() {
  memset(&g_stLogHeader, 0, sizeof(LogHeader_t));
  return 0;
}

#else

OS_FILE s_file_log = NULL;
LogFileInfo_t s_log_file_info = {0};

static int32_t log_file_save_info() {
  OS_FILE f_info = NULL;
  f_info = OS_FOPEN(LOG_INFO_FILE_NAME, "wb");
  if (!f_info) {
    elog_e("log", "log file open failed");
    return -1;
  }
  OS_FWRITE(&s_log_file_info, sizeof(LogFileInfo_t), 1, f_info);
  OS_FCLOSE(f_info);
  return 0;
}

int32_t LogFile_GetHeader() {
  uint32_t file_size = 0;
  bool set_default = false;
  OS_FILE f_info = NULL;
  f_info = OS_FOPEN(LOG_INFO_FILE_NAME, "rb");
  if (!f_info) {
    elog_e("log", "log file open failed");
    return -1;
  }
  file_size = OS_FSIZE(f_info);
  if (file_size < sizeof(LogFileInfo_t)) {
    set_default = true;
  } else {
    OS_FREAD(&s_log_file_info, sizeof(LogFileInfo_t), 1, f_info);
    elog_i("log", "get log info, log files: %d, index: %d",
           s_log_file_info.file_count, s_log_file_info.index);
  }
  if (set_default) {
    memset(&s_log_file_info, 0, sizeof(LogFileInfo_t));
  }
  OS_FCLOSE(f_info);
  return 0;
}

static int32_t log_file_write(void *buf, uint32_t len) {
  uint32_t write_cnt = 0;
  if (!buf) {
    elog_e("log", "file write buffer is null");
    return 0;
  }
  if (!s_file_log) {
    elog_e("log", "log file is invalid");
    return 0;
  }
  write_cnt = (uint32_t)OS_FWRITE(buf, sizeof(uint8_t), len, s_file_log);
  return write_cnt;
}

static int32_t log_file_open() {
  char file_name[32] = {0};
  if (s_log_file_info.file_count < (MAX_FILE_LOG_LEN / MAX_RECORD_LOG_LEN)) {
    s_log_file_info.index = s_log_file_info.file_count;
    s_log_file_info.file_count++;
  } else {
    if (s_log_file_info.index ==
        ((MAX_FILE_LOG_LEN / MAX_RECORD_LOG_LEN) - 1)) {
      s_log_file_info.index = 0;
    } else {
      s_log_file_info.index++;
    }
  }
  sprintf(file_name, LOG_FILE_NAME, s_log_file_info.index);
  s_file_log = OS_FOPEN(file_name, "wb");
  if (!s_file_log) {
    elog_e("log", "log file open failed");
    return -1;
  }
  return 0;
}

static int32_t log_file_close() {
  if (s_file_log) {
    OS_FCLOSE(s_file_log);
    s_file_log = NULL;
  }
  return 0;
}

static int32_t log_file_read(uint32_t addr, void *buf, uint32_t len) {
  uint32_t i = 0;
  char file_name[32] = {0};
  uint32_t index = 0;
  uint32_t file_total_size = 0;
  uint32_t file_index = 0;
  uint32_t offset = addr;
  uint32_t read_cnt = 0;
  uint32_t need_read = len;
  OS_FILE f_log = NULL;
  if (s_log_file_info.file_count == MAX_FILE_COUNT) {
    index = s_log_file_info.index + 1;
    if (index == MAX_FILE_COUNT) {
      index = 0;
    }
  } else {
    index = 0;
  }
  for (i = 0; i < s_log_file_info.file_count; i++) {
    file_total_size += s_log_file_info.file_size[index];
    if (offset >= file_total_size) {
      index++;
      if (index == MAX_FILE_COUNT) {
        index = 0;
      }
      if (index == s_log_file_info.index) {
        break;
      }
      continue;
    }
    file_index = s_log_file_info.file_size[index] - (file_total_size - offset);
    sprintf(file_name, LOG_FILE_NAME, index);
    f_log = OS_FOPEN(file_name, "rb");
    if (!f_log) {
      continue;
    }
    OS_FSEEK(f_log, file_index, SEEK_SET);
    read_cnt += (uint32_t)OS_FREAD((uint8_t *)buf + offset - addr,
                                   sizeof(uint8_t), need_read, f_log);
    offset = addr + read_cnt;
    OS_FCLOSE(f_log);
    f_log = NULL;
    index++;
    need_read = len - read_cnt;
    if (index == MAX_FILE_COUNT) {
      index = 0;
    }
    if ((index == s_log_file_info.index) || (read_cnt == len)) {
      break;
    }
  }
  return read_cnt;
}

int32_t LogFile_SyncCache(char *cache_data, int32_t cache_size) {
  int32_t write_len = 0;
  elog_i("log", "LogFile_SyncCache enter, cache_size:%d.\n", cache_size);
  if (log_file_open() != 0) {
    elog_e("log", "get header: open log file failed");
    return -1;
  }

  write_len = log_file_write(cache_data, cache_size);
  if (write_len < 0) {
    elog_e("log", "write log file failed");
    return -1;
  }
  s_log_file_info.file_size[s_log_file_info.index] = write_len;

  log_file_close();
  elog_e("log", "index: %d, size: %d.\n", s_log_file_info.index,
         s_log_file_info.file_size[s_log_file_info.index]);
  log_file_save_info();
  return 0;
}

int32_t LogFile_GetData(char *buffer, uint32_t buff_size, uint32_t log_start,
                        uint32_t log_count) {
  uint32_t log_total = LogFile_GetCount();
  uint32_t read_count = 0;
  if ((!buffer) || (buff_size == 0)) {
    elog_e("log", "buffer is not valid");
    return -1;
  }
  if (log_start >= log_total) {
    elog_e("log", "log start address is not valid");
    return -1;
  }
  if (log_start + log_count > log_total) {
    read_count = log_total - log_start;
  } else {
    read_count = log_count;
  }
  if (read_count * sizeof(LogInfo_t) > buff_size) {
    read_count = buff_size / sizeof(LogInfo_t);
  }
  log_file_read(log_start * sizeof(LogInfo_t), buffer,
                read_count * sizeof(LogInfo_t));
  return read_count * sizeof(LogInfo_t);
}

uint32_t LogFile_GetCount() {
  uint32_t i = 0;
  uint32_t total_size = 0;
  for (i = 0; i < s_log_file_info.file_count; i++) {
    total_size += s_log_file_info.file_size[i];
  }
  return total_size / sizeof(LogInfo_t);
}

uint32_t LogFile_Clear() {
  char file_name[32] = {0};
  for (uint32_t i = 0; i < s_log_file_info.file_count; i++) {
    sprintf(file_name, LOG_FILE_NAME, i);
    OS_FREMOVE(file_name);
  }
  elog_i("log", "delete all log files");

  memset(&s_log_file_info, 0, sizeof(LogFileInfo_t));
  log_file_save_info();
  return 0;
}

#endif
